package com.core.base.searchForm.support;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.core.base.searchForm.ISearchForm;
import com.core.base.searchForm.ISearchForm.SearchOrder;
import com.core.base.searchForm.annotation.SearchProp;
import com.core.base.searchForm.enums.SearchType;
import com.core.base.searchForm.hql.IBaseHQLSearchForm;
import com.core.page.Pager;
import com.core.page.PagerResult;
import com.core.util.ClassUtil;
import com.core.util.CollectionUtils;
import com.core.util.CommonUtil;

/**
 * 
 * 对定义的方法进行实现
 * 
 * @author pt-chenqw
 * 
 */
@SuppressWarnings({"unchecked","hiding"})
public class SimpleSearchFormSupport<T> implements ISearchFormSupport<T> {
	
	private Class<T> persistentClass;
	
	public Class<T> getPersistentClass() {
		return persistentClass;
	}

	public void setPersistentClass(Class<T> persistentClass) {
		this.persistentClass = persistentClass;
	}

	@Override
	public <T> PagerResult<T> searchByHQLSearchForm(Class<?> cls,Session session,IBaseHQLSearchForm searchForm){
		
		return findByHQLSearchForm(cls, session, searchForm);
	}
	
	/**
	 * 基于HQL方式的searchform,仅内部调用
	 * @param cls
	 * @param session
	 * @param searchForm
	 * @return
	 */
	private <T> PagerResult<T> findByHQLSearchForm(Class<?> cls,Session session,IBaseHQLSearchForm searchForm){
		List<HQLSearchProp> hqlSearchProps = searchForm.getPropsHQL();
		String hql = searchForm.getHQL();
		hql = hangleHQLByMultiCondition(hqlSearchProps, hql);
		String count = searchForm.getCountHQL();
		//内部拼装的HQL
		String countHQL = null;
		if(count == null){
			//如果实现类未做处理,则内部处理
			countHQL = handleCountHQL(hql);
		}else{
			countHQL = count;
		}
		//对查询返回的总行数进行查询
		Query totalQueryObject = session.createQuery(countHQL);
		if(CollectionUtils.notEmpty(hqlSearchProps)){
			for (HQLSearchProp hqlSearchProp : hqlSearchProps) {
				setNameParameterToQuery(totalQueryObject, hqlSearchProp);
			}
		}
		int total = 0;
		List<Number> list = totalQueryObject.list();
		if(list.size() == 1){
			total = list.get(0).intValue();
		}else{
			total = list.size();
		}
		//对查询HQL进行查询
		Query query = session.createQuery(hql);
		if(CollectionUtils.notEmpty(hqlSearchProps)){
			for (HQLSearchProp hqlSearchProp : hqlSearchProps) {
				setNameParameterToQuery(query, hqlSearchProp);
			}
		}
		PagerResult<T> pagerResult = new PagerResult<>();
		int pageSize = searchForm.getPageSize();
		Pager pager = new Pager(total, pageSize, searchForm.getCurrentPage());
		query.setFirstResult(pager.getStart());
		int max = Math.min(pageSize, total);
		query.setMaxResults(max);
		pagerResult.setPager(pager);
		pagerResult.setResults(query.list());
		return pagerResult;
	}
	
	/**
	 * 组装HQL
	 * @param hql
	 * @return
	 */
	private String handleCountHQL(String hql){
		String upperCaseHQL = hql.toUpperCase();
		String subHQL = hql.substring(upperCaseHQL.indexOf("FROM"));
		return "SELECT COUNT(*) " + subHQL;
	}
	
	/**
	 * 给查询的条件参数设值
	 * @param query
	 * @param hqlSearchProp
	 */
	private void setNameParameterToQuery(Query query,HQLSearchProp hqlSearchProp){
		String paramName = hqlSearchProp.getParamName();
		Object value = hqlSearchProp.getValue();
		SearchType searchType = hqlSearchProp.getSearchType();
		if(value.getClass().isArray() && Array.getLength(value) == 0){
			return;
		}
		if(value instanceof Collection){
			if(((Collection<?>) value).size() == 0){
				return;
			}
			query.setParameterList(paramName, (Collection<?>) value);
			return;
		}
		if(value instanceof Object[]){
			if(((Object[]) value).length == 0){
				return;
			}
		}
		if(searchType == SearchType.LIKE_WITH_END){
			value =  "%" + value;
		}
		if(searchType == SearchType.LIKE_WITH_START){
			value += "%";
		}
		if(searchType == SearchType.LIKE_WITH_ANYWHERE){
			value =  "%" + value + "%";
		}
		//适用于使用临时变量的SQL
		query.setParameter(paramName, value);
		
	}
	
	/**
	 * 根据HQL和属性进行SQL封装
	 * 在查询条件的两边加上#字段值#
	 * 如：
	 * from Users t where #phone# t.phone = :phone #phone#
	 * @param hqlSearchProps
	 * @param hql
	 * @return
	 */
	private String hangleHQLByMultiCondition(List<HQLSearchProp> hqlSearchProps,String hql){
		//第一个#出现的位置
		int first = hql.indexOf("#");
		if(first < 0){
			return hql;
		}
		//第二个#出现的位置
		int second = hql.indexOf("#",first + 1);
		if(second < 0){
			return hql;
		}
		//两个#号中间的字段名称
		String name = hql.substring(first + 1, second);
		if(isNullOrDefault(hqlSearchProps, name)){
			//如果HQL的字段未设置值,则将#name#条件#name#去掉
			String regexp = "#" + name + "#[\\s\\S]*?#" + name + "#";
			hql = hql.replaceAll(regexp, "");
		}else{
			//如果有值的话将#name#去掉
			hql = hql.replace("#" + name + "#", "");
		}
		//如果有多个条件,则继续调用此方法-->递归
		if(hql.indexOf("#") > 0){
			return hangleHQLByMultiCondition(hqlSearchProps, hql);
		}
		return hql;
	}
	
	/**
	 * 判断hqlSearchProps是否为空
	 * 和paramName是否匹配
	 * @param hqlSearchProps
	 * @param paramName
	 * @return
	 */
	private boolean isNullOrDefault(List<HQLSearchProp> hqlSearchProps,String paramName){
		if(CollectionUtils.empty(hqlSearchProps)){
			return true;
		}
		for (HQLSearchProp hqlSearchProp : hqlSearchProps) {
			if(hqlSearchProp == null){
				continue;
			}
			Object value = hqlSearchProp.getValue();
			if(value == null){
				continue;
			}
			if(!hqlSearchProp.getParamName().equals(paramName)){
				continue;
			}
			if(value instanceof Collection || value.getClass().isArray() || value instanceof Map){
				Collection<?> col = CollectionUtils.toCollection(value);
				if(CollectionUtils.empty(col)){
					return true;
				}
			}
			if(!CommonUtil.isNullOrDefaultValue(hqlSearchProp.getValue(), hqlSearchProp.getNullValue())){
				return false;
			}
		}
		return true;
	}
	
	@Override
	public <T> PagerResult<T> findBySearchForm(final Class<?> searchClass, Session session,final ISearchForm searchForm){
		return search(searchClass, session, searchForm);
	}
	
	/**
	 * 根据注解SearchProp把字段放入Criterion中
	 * @param fields
	 * @param criteria
	 * @param searchForm
	 */
	private void setCriterionByFields(List<Field> fields,Criteria criteria,final ISearchForm searchForm){
		for (Field field : fields) {
			//获取属性名
			String fieldName = field.getName();
			//获取注解对象
			SearchProp searchProp = field.getAnnotation(SearchProp.class);
			//获取属性值
			Object fieldValue = ClassUtil.getFieldValue(searchForm, field);
			if(searchProp == null){
				continue;
			}
			SearchType searchType = searchProp.searchType();
			
			//如果值为空则忽略
			if(CommonUtil.isNullOrDefaultValue(fieldValue)){
				continue;
			}
			getSearchType(searchType, fieldValue, fieldName, criteria);
		}
	}
	
	/**
	 * 基于不使用HQL查询,仅内部调用
	 * @param searchClass
	 * @param session
	 * @param searchForm
	 * @return
	 */
	private <T> PagerResult<T> search(final Class<?> searchClass, Session session,final ISearchForm searchForm) {
		Criteria criteria = session.createCriteria(searchClass);
		Class<? extends ISearchForm> clazz = searchForm.getClass();
		/**
		 * 根据searchform中定义的条件属性进行查询
		 */
		List<Field> fields = ClassUtil.getAllDeclaredFields(clazz);
		setCriterionByFields(fields, criteria, searchForm);
		//设置排序
		Map<String, SearchOrder> orderBys = searchForm.getOrderBys();
		if(!CollectionUtils.empty(orderBys)){
			for(Map.Entry<String, SearchOrder> entry : orderBys.entrySet()){
				//排序的字段
				String fieldName = entry.getKey();
				//排序的方式-降序还是升序
				SearchOrder searchOrder = entry.getValue();
				if(SearchOrder.DESC == searchOrder){
					criteria.addOrder(Order.desc(fieldName));
				}else{
					criteria.addOrder(Order.asc(fieldName));
				}
			}
		}
		/**
		 * 得到分页信息
		 * 总行数        total
		 * 每页行数    pageSize
		 * 当前页数    currentPage
		 */
		int pageSize = searchForm.getPageSize();
		//获取返回的总行数,不使用criteria.list(),考虑到分页,如果这里用了,则后面无法分页
		Long total =  (Long)criteria.setProjection(Projections.rowCount()).uniqueResult();
		//需要将Projection置空,否则无法正常分页
		criteria.setProjection(null);
		//设置分页器
		Pager pager = new Pager(total.intValue(), pageSize, searchForm.getCurrentPage());
		criteria.setFirstResult(pager.getStart());
		//每页的显示数,如果是第一页,则显示firstResult和maxResult的值
		int max = Math.min(pageSize, total.intValue());
		criteria.setMaxResults(max);
		PagerResult<T> pagerResult = new PagerResult<>();
		pagerResult.setPager(pager);
		List<T> list = criteria.list();
		pagerResult.setResults(list);
		return pagerResult;
	}
	
	/**
	 * 根据searchtype将查询条件和值放入criteria中
	 * @param searchType
	 * @param fieldValue
	 * @param fieldName
	 * @param criteria
	 */
	private void getSearchType(SearchType searchType,Object fieldValue,String fieldName,Criteria criteria){
		MatchMode matchMode = searchType.getMatchMode();
		switch(searchType){
		// like 比较
		case LIKE_WITH_START:
		case LIKE_WITH_END:
		case LIKE_WITH_EXACT:
		case LIKE_WITH_ANYWHERE:
			criteria.add(Restrictions.like(fieldName, (String) fieldValue, matchMode));
			break;
		// not like 比较
		case NOT_LIKE_WITH_START:
		case NOT_LIKE_WITH_END:
		case NOT_LIKE_WITH_EXACT:
		case NOT_LIKE_WITH_ANYWHERE:
			criteria.add(Restrictions.not(Restrictions.like(fieldName, (String) fieldValue, matchMode)));
			break;
		// ilike 比较
		case ILIKE_WITH_START:
		case ILIKE_WITH_EXACT:
		case ILIKE_WITH_END:
		case ILIKE_WITH_ANYWHERE:
			criteria.add(Restrictions.ilike(fieldName, (String) fieldValue, matchMode));
			break;
		case EQ:
			criteria.add(Restrictions.eq(fieldName, fieldValue));
			break;
		case NE:
			criteria.add(Restrictions.ne(fieldName, fieldValue));
			break;
		case LT:
			criteria.add(Restrictions.lt(fieldName, fieldValue));
			break;
		case LE:
			criteria.add(Restrictions.le(fieldName, fieldValue));
			break;
		case GT:
			criteria.add(Restrictions.gt(fieldName, fieldValue));
			break;
		case GE:
			criteria.add(Restrictions.ge(fieldName, fieldValue));
			break;
		case IS_NULL:
			criteria.add(Restrictions.isNull(fieldName));
			break;
		case IS_NOT_NULL:
			criteria.add(Restrictions.isNotNull(fieldName));
			break;
		case NULL_OR_EQ:
			criteria.add(Restrictions.or(Restrictions.isNull(fieldName),Restrictions.eq(fieldName, fieldValue)));
			break;
		case NULL_OR_EMPTY:
			criteria.add(Restrictions.or(Restrictions.isNull(fieldName),Restrictions.isEmpty(fieldName)));
			break;
		case NULL_OR_NOT_LIKE_WITH_ANYWHERE:
			criteria.add(Restrictions.or(Restrictions.isNull(fieldName),Restrictions.not(Restrictions.like(fieldName, fieldName, MatchMode.ANYWHERE))));
			break;
		case NOT_EMPTY_STRING:
			criteria.add(Restrictions.not(Restrictions.eq(fieldName, "")));
			break;
		case EMPTY_STRING:
			criteria.add(Restrictions.eq(fieldName, ""));
			break;
		case IS_NOT_NULL_AND_NOT_EMPTY_STRING:
			criteria.add(Restrictions.and(Restrictions.isNotNull(fieldName),Restrictions.not(Restrictions.eq(fieldName, ""))));
			break;
		case IS_NULL_AND_EMPTY_STRING:
			criteria.add(Restrictions.and(Restrictions.isNull(fieldName),Restrictions.eq(fieldName, "")));
			break;
		}
	}


}
